﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using MyBasicLib.Extensions;
using MyBasicLib.Linq.Expressions;
using System.Linq;
using System.Linq.Expressions;

namespace MyBasicLib.Data.ORMLiteNew.SqlClient
{
    public class WhereTranslator : ExpressionTranslator
    {
        private void GetStringMethod(MethodCallExpression m)
        {
            string wildCard = "%";
            var me = m.Object.TrimUnary();
            var list = new List<object>
                           {
                               string.Format("[{0}].[{1}]", TableAliasCache.GetTableAlias(me),
                                             ((MemberExpression) me).Member.Name)
                           };

            if (m.Object.Type == typeof(string))
            {
                list.AddRange(m.Arguments.Select(arg => arg.Eval()));
            }
            else
            { 
                Sb.Append("DATEADD(");
                if (m.Method.Name == "AddYears")
                    Sb.Append("YEAR,"); 
                else if (m.Method.Name == "AddHours")
                    Sb.Append("HOUR,");
                Visit(m.Arguments[0]);
                Sb.Append(",");
                Sb.Append(list[0]);
                Sb.Append(")");
                return;
            }
            object[] operands = list.ToArray();
            switch (m.Method.Name)
            {
                
                case "StartsWith":
                    Debug.Assert(operands.Length == 2);
                    Sb.Append(operands[0]);
                    Sb.Append(" LIKE ");
                    HandleParameter(Sb, operands[1] + wildCard);
                    break;
                case "EndsWith":
                    Debug.Assert(operands.Length == 2);
                    Sb.Append(operands[0]);

                    Sb.Append(" LIKE ");
                    HandleParameter(Sb, wildCard + operands[1]);
                    break;
                case "Contains":
                    Debug.Assert(operands.Length == 2);
                    Sb.Append(operands[0]);
                    Sb.Append(" LIKE ");
                    HandleParameter(Sb, wildCard + operands[1] + wildCard);
                    break;
                case "ToUpper":
                    Debug.Assert(operands.Length == 1);
                    Sb.Append("UPPER(");
                    Sb.Append(operands[0]);
                    Sb.Append(")");
                    break;
                case "ToLower":
                    Debug.Assert(operands.Length == 1);
                    Sb.Append("LOWER(");
                    Sb.Append(operands[0]);
                    Sb.Append(")");
                    break;
                case "CompareTo":
                    Debug.Assert(operands.Length == 2);
                    Sb.Append(operands[0]);
                    break;

                default:
                    throw new NotSupportedException(m.Method.Name);
            }
        }


        protected override Expression VisitMethodCall(MethodCallExpression m)
        {
            if (m.Object == null)
            {
                if (m.Method.DeclaringType == typeof(SqlMethods))
                {
                    if (m.Method.Name == "Len")
                    {
                        string table = TableAliasCache.GetTableAlias(m.Arguments[0]);
                        string col = ((MemberExpression)m.Arguments[0]).Member.Name;
                        Sb.AppendFormat("LEN([{0}].[{1}])", table, col);
                    }
                    if (m.Method.Name == "IsNull")
                    {
                        string table = TableAliasCache.GetTableAlias(m.Arguments[0]);
                        string col = ((MemberExpression)m.Arguments[0]).Member.Name;
                        Sb.AppendFormat("[{0}].[{1}] IS NULL", table, col);
                    }
                    if (m.Method.Name == "FullTextContains")
                    {
                        string table = TableAliasCache.GetTableAlias(m.Arguments[0]);

                        string col = ((MemberExpression)m.Arguments[0]).Member.Name;
                        Sb.AppendFormat("CONTAINS ([{0}].[{1}],", table, col);
                        HandleParameter(Sb, m.Arguments[1].Eval());
                        Sb.Append(")");
                    }
                    return m;
                }
            }
            if (m.Object != null && (m.Object.Type == typeof(string) || m.Object.Type == typeof(DateTime)))
            {
                GetStringMethod(m);
                return m;
            }
            if (m.Method.Name == "Contains")
            {
                object list;
                string col;
                if (m.Arguments.Count == 2)
                {
                    list = m.Arguments[0].Eval();
                    col = string.Format("[{1}].[{0}]", ((MemberExpression)m.Arguments[1]).Member.Name,
                                        TableAliasCache.GetTableAlias(m.Arguments[1]));
                }
                else if (m.Object != null)
                {
                    list = m.Object.Eval();

                    col = string.Format("[{1}].[{0}]", ((MemberExpression)m.Arguments[0]).Member.Name,
                                        TableAliasCache.GetTableAlias(m.Arguments[0]));
                }
                else
                {
                    return m;
                }
                var tmpArr = list as IEnumerable; //tmpArr

                int idx = Sb.Length;
                Sb.Append(col);
                Sb.Append(" IN (");
                int ins = 0;
                foreach (object p in tmpArr)
                {
                    HandleParameter(Sb, p);
                    Sb.Append(",");
                    ins++;
                }
                if (ins > 0)
                {
                    Sb.Length--;
                    Sb.Append(")");
                }
                else
                {
                    Sb.Length = idx;
                    Sb.Append("0 = 1");
                }
            }
            return m;
        }

        protected override Expression VisitUnary(UnaryExpression u)
        {
            //if (u.NodeType == ExpressionType.Not)
            //{
            //    base.Sb.Append(" NOT ");

            //}
            if (Sb[Sb.Length - 1] == ' ') Sb.Length--;
            AppendNodeType(u, Sb);
            var shouldAddBracket = ShouldAddBracket(u, u.Operand);
            VisitOperand(u.Operand, shouldAddBracket);
            return u;
        }
    }
     
}